﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Reflection;

using Bio.IO.GenBank;
using System.Collections.ObjectModel;

namespace SequenceViewer
{
    /// <summary>
    /// Interaction logic for TestUserControl.xaml
    /// </summary>
    public partial class AnnotationMaster : UserControl
    {
        List<string> Qualifiers = null;

        MainWindow _ownerWindow = null;
        long _seqMax = 0;
        FeatureItem _selectedFeatureItem = null;
        bool _addMode = true;

        public AnnotationMaster()
        {
            InitializeComponent();
        }

        public void Initialize(
            bool addMode,
            MainWindow owner,
            int start,
            int end,
            long max,
            FeatureItem fi)
        {
            _addMode = addMode;
            _ownerWindow = owner;
            txtStart.Text = start == 0 ? string.Empty : start.ToString();
            txtEnd.Text = end == 0 ? string.Empty : end.ToString();
            _seqMax = max;
            _selectedFeatureItem = fi;

            if (_addMode)
            {
                cbKey.IsEnabled = true;
                txtStart.IsEnabled = true;
                txtEnd.IsEnabled = true;
            }
            else
            {
                txtStart.IsEnabled = false;
                txtEnd.IsEnabled = false;

                cbKey.SelectedItem = fi.Key;
                cbKey.IsEnabled = false;

                txtLabel.Text = fi.Label.Replace("\"", "");

                if (Qualifiers != null)
                {
                    foreach (KeyValuePair<string, List<string>> kvp in fi.Qualifiers)
                    {
                        if (Qualifiers.Contains(kvp.Key.Replace("\"", "")))
                            AddQualifierRow(kvp.Key.Replace("\"", ""), kvp.Value);
                    }
                }
            }
        }

        private void AnnotationMaster_Loaded(object sender, RoutedEventArgs e)
        {
            Type featureKeys = typeof(StandardFeatureKeys);

            var keys = from f in featureKeys.GetFields(BindingFlags.Public | BindingFlags.Static)
                       where f.IsLiteral == true && f.IsInitOnly == false
                       orderby f.Name
                       select f.GetRawConstantValue();

            cbKey.ItemsSource = keys;
        }

        private void cbKey_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            DeleteChildrenRecursive(stkPanel);

            string selectedKey = cbKey.SelectedItem as string;

            if (string.IsNullOrEmpty(selectedKey))
                return;

            FeatureItem fi = new FeatureItem(selectedKey, new Location());

            var realFeatureItem = StandardFeatureMap.GetStandardFeatureItem(fi);

            Type t = realFeatureItem.GetType();

            Type t2 = typeof(StandardQualifierNames);

            var props = from p in t.GetProperties()
                        from q in t2.GetFields(BindingFlags.Public | BindingFlags.Static)
                        where p.Name != "Key" && p.Name != "Label" &&
                            p.Name != "Location" && p.Name != "Qualifiers" &&
                            q.IsLiteral == true && q.IsInitOnly == false &&
                            p.Name == q.Name
                        orderby q.Name
                        select q.GetRawConstantValue() as string;

            Qualifiers = props.ToList();
        }

        private void Hyperlink_Click(object sender, RoutedEventArgs e)
        {
            AddQualifierRow(string.Empty, null);
        }

        private void AddQualifierRow(string qualifierKey, List<string> qualifierValue)
        {
            Border border = new Border();
            border.BorderBrush = new SolidColorBrush(Colors.Brown);
            border.BorderThickness = stkPanel.Children.Count == 0 ? new Thickness(0.25) : new Thickness(0.25, 0, 0.25, 0.25);

            Grid g = new Grid();
            g.Margin = new Thickness(2, 0, 2, 0);

            g.ColumnDefinitions.Add(new ColumnDefinition());
            g.ColumnDefinitions.Add(new ColumnDefinition());
            g.ColumnDefinitions.Add(new ColumnDefinition());

            g.RowDefinitions.Add(new RowDefinition());

            ComboBox c = new ComboBox();
            c.ItemsSource = Qualifiers;
            c.SelectionChanged += new SelectionChangedEventHandler(c_SelectionChanged);
            g.Children.Add(c);
            Grid.SetColumn(c, 0);
            c.VerticalAlignment = System.Windows.VerticalAlignment.Center;
            c.Margin = new Thickness(2);

            Grid ng = new Grid();
            ng.ColumnDefinitions.Add(new ColumnDefinition());
            ng.ColumnDefinitions.Add(new ColumnDefinition());
            ng.RowDefinitions.Add(new RowDefinition());
            ng.ColumnDefinitions[0].Width = new GridLength(10, GridUnitType.Star);
            ng.ColumnDefinitions[1].Width = new GridLength(10, GridUnitType.Auto);
            ng.Margin = new Thickness(2);

            TextBox txt = new TextBox();
            txt.VerticalAlignment = System.Windows.VerticalAlignment.Center;
            txt.Margin = new Thickness(2, 0, 2, 0);

            StackPanel sp = new StackPanel();
            sp.VerticalAlignment = System.Windows.VerticalAlignment.Center;
            sp.Children.Add(txt);

            ng.Children.Add(sp);
            Grid.SetColumn(sp, 0);

            Hyperlink hl = new Hyperlink(new Run("Add"));
            hl.Click += new RoutedEventHandler(hl_Click);
            hl.TextDecorations = null;

            TextBlock tb = new TextBlock(hl);
            tb.VerticalAlignment = System.Windows.VerticalAlignment.Center;
            tb.Visibility = System.Windows.Visibility.Collapsed;
            tb.Margin = new Thickness(2);
            ng.Children.Add(tb);
            Grid.SetColumn(tb, 1);

            g.Children.Add(ng);
            Grid.SetColumn(ng, 1);

            Button b = new Button();
            b.Margin = new Thickness(2);
            b.Content = "Remove";
            g.Children.Add(b);
            Grid.SetColumn(b, 2);
            b.VerticalAlignment = System.Windows.VerticalAlignment.Center;
            b.Click += new RoutedEventHandler(b_Click);

            g.ColumnDefinitions[0].Width = new GridLength(140);
            g.ColumnDefinitions[1].Width = new GridLength(10, GridUnitType.Star);
            g.ColumnDefinitions[2].Width = new GridLength(10, GridUnitType.Auto);

            g.RowDefinitions[0].Height = new GridLength(10, GridUnitType.Auto);

            border.Child = g;
            stkPanel.Children.Add(border);

            if (qualifierKey != string.Empty)
            {
                c.SelectedItem = qualifierKey;
                bool val = false;

                StackPanel panel = (StackPanel)((Grid)((Grid)c.Parent).Children[1]).Children[0];

                if (qualifierValue.Count > 1)
                {
                    ((TextBox)panel.Children[0]).Text = qualifierValue[0];

                    for (int i = 1; i < qualifierValue.Count; i++)
                    {
                        panel.Children.Add(new TextBox());
                        ((TextBox)panel.Children[i]).Text = qualifierValue[i];
                    }
                }
                else if (panel.Children[0] is CheckBox)
                {
                    Boolean.TryParse(qualifierValue[0], out val);
                    ((CheckBox)panel.Children[0]).IsChecked = val;
                }
                else
                {
                    ((TextBox)panel.Children[0]).Text = qualifierValue[0];
                }
            }
        }

        void b_Click(object sender, RoutedEventArgs e)
        {
            stkPanel.Children.Remove(((Grid)((Button)sender).Parent).Parent as Border);
        }

        void hl_Click(object sender, RoutedEventArgs e)
        {
            ((StackPanel)((Grid)((TextBlock)((Hyperlink)sender).Parent).Parent).Children[0]).Children.Add(new TextBox());
        }

        void c_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            Grid g = ((ComboBox)sender).Parent as Grid;
            
            if (g == null)
                MessageBox.Show("NULL");
            else
            {
                string selectedQualifier = ((ComboBox)sender).SelectedItem as string;

                string selectedKey = cbKey.SelectedItem as string;

                FeatureItem fi = new FeatureItem(selectedKey, new Location());

                var realFeatureItem = StandardFeatureMap.GetStandardFeatureItem(fi);

                Type t = realFeatureItem.GetType();

                PropertyInfo pi = t.GetProperty(
                    (from q in typeof(StandardQualifierNames).GetFields(BindingFlags.Public | BindingFlags.Static)
                    where q.GetRawConstantValue() as string == selectedQualifier
                    select q.Name).Single()
                        );

                TextBlock tb = (TextBlock)((Grid)((Grid)((ComboBox)sender).Parent).Children[1]).Children[1];

                if (pi.PropertyType.IsGenericType)
                    tb.Visibility = System.Windows.Visibility.Visible;
                else
                    tb.Visibility = System.Windows.Visibility.Collapsed;

                StackPanel innerStack = (StackPanel)((Grid)((Grid)((ComboBox)sender).Parent).Children[1]).Children[0];

                innerStack.Children.Clear();

                if ((pi.PropertyType.IsGenericType || pi.PropertyType == typeof(string)))
                {
                    TextBox txt = new TextBox();
                    innerStack.Children.Add(txt);
                }
                else if (pi.PropertyType == typeof(Boolean))
                {
                    CheckBox cb = new CheckBox();
                    cb.Margin = new Thickness(5);
                    innerStack.Children.Add(cb);
                }
            }
        }

        void DeleteChildrenRecursive(Panel panel)
        {
            foreach (UIElement uie in panel.Children)
            {
                if (uie is Panel)
                {
                    DeleteChildrenRecursive(uie as Panel);
                }
            }

            panel.Children.Clear();
        }

        private void btnOK_Click(object sender, RoutedEventArgs e)
        {
            List<string> errmsgs = Validate();

            if (errmsgs != null && errmsgs.Count > 0)
            {
                StringBuilder sb = new StringBuilder();

                foreach (string s in errmsgs)
                    sb.AppendLine(s);

                MessageBox.Show(sb.ToString());

                return;
            }

            string selectedKey = cbKey.SelectedItem as string;
            string label = txtLabel.Text;
            string locStart = txtStart.Text;
            string locEnd = txtEnd.Text;

            FeatureItem fi;

            if (_addMode)
            {
                fi = new FeatureItem(selectedKey, new LocationBuilder().GetLocation(locStart + ".." + locEnd));
            }
            else
            {
                fi = _selectedFeatureItem;
                fi.Qualifiers.Clear();
            }

            if (!string.IsNullOrEmpty(label))
                fi.Label = "\"" + label + "\"";
            
            var realFeatureItem = StandardFeatureMap.GetStandardFeatureItem(fi);
            Type t = realFeatureItem.GetType();

            foreach (Border b in stkPanel.Children)
            {
                Grid g = b.Child as Grid;

                string qualifier = ((ComboBox)g.Children[0]).SelectedItem as string;
                PropertyInfo pi = t.GetProperty(
                        (from q in typeof(StandardQualifierNames).GetFields(BindingFlags.Public | BindingFlags.Static)
                         where q.GetRawConstantValue() as string == qualifier
                         select q.Name).Single()
                            );

                if (pi.PropertyType == typeof(string))
                {
                    string value = ((TextBox)((StackPanel)((Grid)g.Children[1]).Children[0]).Children[0]).Text;
                    if(!string.IsNullOrEmpty(value))
                        AddQualifierToFeature(fi, qualifier, value);
                }
                else if (pi.PropertyType == typeof(Boolean))
                {
                    string value = ((CheckBox)((StackPanel)((Grid)g.Children[1]).Children[0]).Children[0]).IsChecked.ToString();
                    if (!string.IsNullOrEmpty(value))
                        AddQualifierToFeature(fi, qualifier, value);
                }
                else
                {
                    StackPanel stk = (StackPanel)((Grid)g.Children[1]).Children[0];
                    List<string> values = new List<string>();
                    foreach (TextBox tb in stk.Children)
                    {
                        if (!string.IsNullOrEmpty(tb.Text))
                            AddQualifierToFeature(fi, qualifier, tb.Text);
                    }
                }
            }

            _ownerWindow.CurrentFeatureItem = fi;

            this.Visibility = System.Windows.Visibility.Hidden;
        }

        private List<string> Validate()
        {
            int locStart, locEnd;

            List<string> errmsgs = new List<string>();

            if (string.IsNullOrEmpty(cbKey.SelectedItem as string))
                errmsgs.Add("Key is mandatory.");

            //if (string.IsNullOrEmpty(txtLabel.Text))
            //    errmsgs.Add("Label/Name is mandatory.");

            if (string.IsNullOrEmpty(txtStart.Text))
                errmsgs.Add("Location Start (nucleotide) is mandatory.");

            if (string.IsNullOrEmpty(txtEnd.Text))
                errmsgs.Add("Location End (nucleotide) is mandatory.");

            if (!int.TryParse(txtStart.Text, out locStart) || locStart <= 0 || locStart >= _seqMax)
                errmsgs.Add("Location Start (nucleotide) must be a positive integer and less than sequence length.");

            if (!int.TryParse(txtEnd.Text, out locEnd) || locEnd <= locStart || locEnd > _seqMax)
                errmsgs.Add("Location End (nucleotide) must be a positive integer, greater than Location Start and less than or equal to sequence length.");

            return errmsgs;
        }

        private void btnCancel_Click(object sender, RoutedEventArgs e)
        {
            _ownerWindow.CurrentFeatureItem = null;
            this.Visibility = System.Windows.Visibility.Hidden;
        }

        private void AddQualifierToFeature(FeatureItem feature, string qualifierKey, string qualifierValue)
        {
            if (!feature.Qualifiers.ContainsKey(qualifierKey))
            {
                feature.Qualifiers[qualifierKey] = new List<string>();
            }

            feature.Qualifiers[qualifierKey].Add(qualifierValue);
        }

        private void UserControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if ((bool)e.NewValue == true)
            {
                cbKey.SelectedIndex = -1;
                txtLabel.Text = string.Empty;
                txtStart.Text = string.Empty;
                txtEnd.Text = string.Empty;
                Qualifiers = null;
            }
        }
    }
}
